package com.fw4j.jdbc.tools;

import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.GeneratorAdapter;
import org.objectweb.asm.commons.Method;
import org.objectweb.asm.util.CheckClassAdapter;
import org.objectweb.asm.util.TraceClassVisitor;
import org.omg.CORBA.INTF_REPOS;

/**
 * @author 穆仁超
 * 动态生成Dao类
 * 日期 2013-12-27 16:34
 * 我的QQ群：32894018
 */
public class CreateDao extends ClassLoader{
	private ClassWriter cw1 = null;
	private TraceClassVisitor cw = null;
	private GeneratorAdapter ga=null;
	private MethodVisitor mv = null;
	private String className = null;
	private Method m = null;
	private static java.util.Map<String,byte[]> bytecodes = new java.util.HashMap<String,byte[]>();
	
	public CreateDao() throws FileNotFoundException{
		cw1 = new ClassWriter(ClassWriter.COMPUTE_MAXS);
		cw = new TraceClassVisitor(cw1, new PrintWriter(System.out));
	}
	
	public void createClass(String className, String extendsClass,
			String[] extendsInterfaces,Class cl) {
		if(extendsClass == null) extendsClass = "java/lang/Object";
		this.className = className;
		cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC, this.className, null,extendsClass, extendsInterfaces);
		Method m = Method.getMethod("void <init> ()");
		ga = new GeneratorAdapter(Opcodes.ACC_PUBLIC, m, null,null,cw);
		ga.loadThis();
		ga.invokeConstructor(Type.getType(extendsClass), m);
		ga.loadThis();
		ga.visitLdcInsn(Type.getType(cl));
		ga.putField(Type.getType(this.className), "cl", Type.getType(Class.class));
		ga.returnValue();
		ga.endMethod();
	}
		
	public void createMethod(String methodName){
		m = Method.getMethod(methodName,true);
		ga = new GeneratorAdapter(Opcodes.ACC_PUBLIC, m, null,null,cw);
	}
	public void addString(String value){
		ga.loadThis();
		ga.visitLdcInsn(value);
		ga.putField(Type.getType(this.className),"sql",Type.getType(String.class));
	}
	public void addList(){
		ga.loadThis();
		ga.newInstance(Type.getType(ArrayList.class));
		ga.dup();
		ga.invokeConstructor(Type.getType(ArrayList.class),Method.getMethod("void <init> ()"));
		ga.putField(Type.getType(this.className),"paramsList",Type.getType(List.class));
	}
	public void ListAdd(Class cl,int num){
		ga.loadThis();
		ga.getField(Type.getType(this.className),"paramsList",Type.getType(List.class));
		if(cl == int.class){
			ga.loadArg(num);
			ga.invokeStatic(Type.getType(Integer.class), Method.getMethod("Integer valueOf (int)"));
		}else if(cl == char.class){
			ga.loadArg(num);
			ga.invokeStatic(Type.getType(Character.class), Method.getMethod("Character valueOf (char)"));
		}else if(cl == short.class){
			ga.loadArg(num);
			ga.invokeStatic(Type.getType(Short.class), Method.getMethod("Short valueOf (short)"));
		}else if(cl == long.class){
			ga.loadArg(num);
			ga.invokeStatic(Type.getType(Long.class), Method.getMethod("Long valueOf (long)"));
		}else if(cl == double.class){
			ga.loadArg(num);
			ga.invokeStatic(Type.getType(Double.class), Method.getMethod("Double valueOf (double)"));
		}else if(cl == float.class){
			ga.loadArg(num);
			ga.invokeStatic(Type.getType(Float.class), Method.getMethod("Float valueOf (float)"));
		}else{
			ga.loadArg(num);
		}
		ga.invokeInterface(Type.getType(List.class), Method.getMethod("boolean add (Object)"));
		ga.pop();
	}
	public void callFind(Class retype){
		ga.loadThis();
		ga.loadThis();
		ga.getField(Type.getType(this.className),"sql",Type.getType(String.class));
		ga.loadThis();
		ga.getField(Type.getType(this.className),"paramsList",Type.getType(List.class));
		ga.invokeVirtual(Type.getType(this.className), Method.getMethod("java/lang/Object fw4j_dao_find (java/lang/String,java/util/List)",true));
		if(retype != Object.class){
			ga.checkCast(Type.getType(retype));
		}
	}
	public void callModify(){
		ga.loadThis();
		ga.loadThis();
		ga.getField(Type.getType(this.className),"sql",Type.getType(String.class));
		ga.loadThis();
		ga.getField(Type.getType(this.className),"paramsList",Type.getType(List.class));
		ga.invokeVirtual(Type.getType(this.className), Method.getMethod("java/lang/Integer fw4j_dao_modify (java/lang/String,java/util/List)",true));
	}
	public void endMethod(){
		ga.returnValue();
		ga.endMethod();
	}
	
	public Object loadClass(){
		cw.visitEnd();
		try {
			byte[] code = cw1.toByteArray();
			bytecodes.put(this.className, code);
			Class cl = this.defineClass(this.className,code, 0, code.length);
			return cl.newInstance();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public Object loadClass(java.sql.Connection conn){
		cw.visitEnd();
		try {
			byte[] code = cw1.toByteArray();
			bytecodes.put(this.className, code);
			Class cl = this.defineClass(this.className,code, 0, code.length);
			Constructor cons  = cl.getConstructor(java.sql.Connection.class);
			return cons.newInstance(conn);
			//return cl.newInstance();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public Object getInstance(String key){
		if(bytecodes.containsKey(key)){
			try {
				byte[] code = bytecodes.get(key);
				Class cl = this.defineClass(this.className,code, 0, code.length);
				return cl.newInstance();
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
		return null;
	}
}
